home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 January: Mac OS SDK / Dev.CD Jan 98 SDK2.toast / Development Kits (Disc 2) / QuickTime / Sample Code / QT Multiprocessor Support / recompress me.π / recompress me.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-26  |  26.5 KB  |  908 lines  |  [TEXT/CWIE]

  1. #include <Devices.h>
  2. #include <Fonts.h>
  3. #include <LowMem.h>
  4. #include <ImageCompression.h>
  5. #include <Movies.h>
  6. #include <NumberFormatting.h>
  7. #include <Processes.h>
  8.  
  9. Boolean gDecompressingSourceFrame;
  10. Boolean gCompressingSourceFrame;
  11. Boolean gDecompressingToScreen;
  12.  
  13. Boolean getMovieFile(FSSpec *theFile);
  14. OSErr OpenSelection(FSSpecPtr theDocToOpen);
  15.  
  16. pascal void decompressSrcFrameDone(OSErr result, short flags, long refcon);
  17. pascal void compressSrcFrameDone(OSErr result, short flags, long refcon);
  18. pascal void decompressToScreenDone(OSErr result, short flags, long refcon);
  19.  
  20. Boolean fPeriodic( WindowRef theWindow, Rect *whiteRect, TimeValue srcMovieDuration, TimeValue timeNow );
  21. void fSetRGBColor( RGBColor *theColor, short r, short g, short b );
  22. void fPNumToString( float theNum, Str255 theString, short fix );
  23. void fPStrCopy( Str255 s1, Str255 s2 );
  24. void fPStrConcat( Str255 s1, Str255 s2 );
  25.  
  26. #define    MAX_SAMPLES    60
  27.  
  28. typedef struct {
  29.     long    duration;        // in milliseconds
  30.     long    size;            // sample size in bytes
  31. } SampleRecord;
  32.  
  33. typedef struct {
  34.     long    duration;
  35.     long    fileOffset;
  36.     long    dataSize;
  37.     Boolean isKeyFrame;
  38.     char    pad[3];
  39. } SampleEntryRecord, **SampleEntry;
  40.  
  41. void main(void)
  42. {
  43.     OSErr err;
  44.     FSSpec file;
  45.     Movie m;
  46.     short resFref;
  47.     WindowPtr w;
  48.     Rect bounds;
  49.     Track srcVideoTrack = nil;
  50.     Media srcVideoMedia;
  51.     Track dstVideoTrack = nil;
  52.     GWorldPtr gw[3];
  53.     ImageDescriptionHandle srcDesc = nil;
  54.     ImageSequence srcDecoSequences[3];
  55.     TimeValue timeNow;
  56.     TimeValue srcMovieDuration;
  57.     Handle srcVideoData1 = NewHandle(0);
  58.     Handle srcVideoData2 = NewHandle(0);
  59.     CodecFlags outFlags;
  60.     long srcVideoDataSize1, srcVideoDataSize2;
  61.     TimeValue mediaTimeNow;
  62.     Boolean useFirstBufferForCompress;
  63.     ICMCompletionProcRecord srcDecoCompletion, compressCompletion, screenDecoCompletion;
  64.     ImageDescriptionHandle compressedDesc = nil;
  65.     ImageSequence compSequence = 0;
  66.     long maxCompressionBufferSize;
  67.     Handle compressedData1 = nil, compressedData2 = nil;
  68.     long compressedDataSize1 = 0, compressedDataSize2 = 0;
  69.     UInt8 compressedSimilarity1, compressedSimilarity2;
  70.     ImageSequence screenDecoSequence = 0;
  71.     long i;
  72.     long currentCompressGWorldIndex;
  73.     long currentDecompressGWorldIndex;
  74.     DataRateParams dataRateStuff;
  75.     long sampleCount = 0;
  76.     SampleRecord sampleRecords[MAX_SAMPLES];
  77.     long totalDuration = 0, totalSize = 0;
  78.     SampleEntry theSampleRefs = (SampleEntry)NewHandle(0);
  79.     long currentFileOffset = 0;
  80.     short outputFref = 0;
  81.     IOParam    writeBlock;
  82.     short outputResRef = -1;
  83.     Movie newMovie = nil;
  84.     Rect whiteRect;
  85.     FSSpec newFile;
  86.  
  87.     Handle compressedFrameDurations = NewHandle(0);
  88.     Handle compressedFrameSize = NewHandle(0);
  89.  
  90.     InitGraf(&qd.thePort);
  91.     InitFonts();
  92.     InitWindows();
  93.     InitMenus();
  94.     TEInit();
  95.     InitDialogs(0L);
  96.     InitCursor();
  97.     MaxApplZone();
  98.  
  99.     EnterMovies();
  100.  
  101.     if (!getMovieFile(&file))
  102.         return;
  103.  
  104.     SetRect(&bounds, 75, 75, 75+160,75+120);
  105.     w = NewCWindow(nil, &bounds, file.name, false, 0, (WindowPtr)-1,
  106.         true, 0);
  107.     if (!w) return;
  108.  
  109.     SetPort(w);
  110.  
  111.     err = OpenMovieFile(&file, &resFref, fsRdPerm);
  112.     if (err) return;
  113.  
  114.     err = NewMovieFromFile(&m, resFref, nil, (StringPtr)nil,
  115.                 newMovieActive, nil);
  116.     if (err) return;
  117.  
  118.     CloseMovieFile(resFref);
  119.  
  120.     // create the output file
  121.     newFile = file;
  122.     newFile.name[++newFile.name[0]] = '!';
  123.     err = CreateMovieFile(&newFile, 'TVOD', -1, createMovieFileDeleteCurFile, &outputResRef, &newMovie);
  124.     if (err) return;
  125.  
  126.     err = FSpOpenDF(&newFile, fsRdWrPerm, &outputFref);
  127.     if (err) return;
  128.  
  129.     writeBlock.ioResult = 0;
  130.  
  131.     GetMovieBox(m, &bounds);
  132.     OffsetRect(&bounds, -bounds.left, -bounds.top);
  133.     SetMovieBox(m, &bounds);
  134.  
  135.     whiteRect = bounds;
  136.     whiteRect.bottom += 120;
  137.  
  138.     SizeWindow(w, bounds.right, whiteRect.bottom, false);
  139.     ShowWindow(w);
  140.  
  141.     whiteRect.top = whiteRect.bottom;
  142.  
  143.     for (i=0; i<3; i++) {
  144.         err = NewGWorld(&gw[i], 32, &bounds, nil, nil, 0);
  145.         if (err) {
  146.             err = NewGWorld(&gw[i], 32, &bounds, nil, nil, useTempMem);
  147.             if (err) return;
  148.         }
  149.         LockPixels(gw[i]->portPixMap);
  150.     }
  151.  
  152.     srcVideoTrack = GetMovieIndTrackType(m, 1, 'vide', movieTrackEnabledOnly | movieTrackMediaType);
  153.     if (!srcVideoTrack) return;
  154.  
  155.     srcVideoMedia = GetTrackMedia(srcVideoTrack);
  156.     srcDesc = (ImageDescriptionHandle)NewHandleClear(sizeof(ImageDescription));
  157.     if (!srcDesc) return;
  158.     GetMediaSampleDescription(srcVideoMedia, 1, (SampleDescriptionHandle)srcDesc);
  159.  
  160.     for (i=0; i<3; i++) {
  161.         err = DecompressSequenceBegin(&srcDecoSequences[i], srcDesc, gw[i], nil, nil, nil, ditherCopy, nil, 0, codecNormalQuality, anyCodec);
  162.         if (err) return;
  163.     }
  164.  
  165.     srcDecoCompletion.completionProc = NewICMCompletionProc(decompressSrcFrameDone);
  166.     srcDecoCompletion.completionRefCon = (long)&gDecompressingSourceFrame;
  167.     gDecompressingSourceFrame = false;
  168.  
  169.     compressCompletion.completionProc = NewICMCompletionProc(compressSrcFrameDone);
  170.     compressCompletion.completionRefCon = (long)&gCompressingSourceFrame;
  171.     gCompressingSourceFrame = false;
  172.  
  173.     screenDecoCompletion.completionProc = NewICMCompletionProc(decompressToScreenDone);
  174.     screenDecoCompletion.completionRefCon = (long)&gDecompressingToScreen;
  175.     gDecompressingToScreen = false;
  176.  
  177.     compressedDesc = (ImageDescriptionHandle)NewHandle(0);
  178.     if (!compressedDesc) return;
  179.  
  180.     err = CompressSequenceBegin(&compSequence, gw[0]->portPixMap, nil, nil, nil, 24, 'cvid', anyCodec, codecLosslessQuality - 1, codecLosslessQuality - 1, 15, nil, codecFlagUpdatePreviousComp, compressedDesc);
  181.     if (err) return;
  182.  
  183.     err = GetMaxCompressionSize(gw[0]->portPixMap, &gw[0]->portRect, 24, codecLosslessQuality - 1, 'cvid', anyCodec, &maxCompressionBufferSize);
  184.     if (err) return;
  185.  
  186.     compressedData1 = NewHandle(maxCompressionBufferSize);
  187.     compressedData2 = NewHandle(maxCompressionBufferSize);
  188.     if (!compressedData1 || !compressedData2)
  189.         return;
  190.  
  191.     HLockHi(compressedData1);
  192.     HLockHi(compressedData2);
  193.  
  194.     dataRateStuff.dataRate = 260 * 1024;
  195.     dataRateStuff.keyFrameRate = 60;
  196.     dataRateStuff.minSpatialQuality = codecLosslessQuality - 1;
  197.     dataRateStuff.minTemporalQuality = codecLosslessQuality - 1;
  198.     totalDuration = 1000;
  199.     totalSize = dataRateStuff.dataRate;
  200.     sampleRecords[0].size = dataRateStuff.dataRate;
  201.     sampleRecords[0].duration = 1000;
  202.     sampleCount = 1;
  203.  
  204.     srcMovieDuration = GetMovieDuration(m);
  205.     timeNow = 0;
  206.  
  207.     // prime the loop by decompressing the first frame into the first gworld
  208.     mediaTimeNow = TrackTimeToMediaTime(timeNow, srcVideoTrack);
  209.     if (mediaTimeNow == -1) return;
  210.  
  211.     err = GetMediaSample(srcVideoMedia, srcVideoData1, 0, &srcVideoDataSize1, mediaTimeNow, nil, nil, nil, nil, 1, nil, nil);
  212.     if (err) return;
  213.  
  214.     HLock(srcVideoData1);
  215.     err = DecompressSequenceFrameS(srcDecoSequences[0], *srcVideoData1, srcVideoDataSize1, 0, &outFlags, nil);
  216.     if (err) return;
  217.  
  218.     HUnlock(srcVideoData1);
  219.  
  220.     // figure out the next frame time
  221.     GetTrackNextInterestingTime(srcVideoTrack, nextTimeMediaSample | nextTimeIgnoreActiveSegment, timeNow, 1, &timeNow, nil);
  222.     if (timeNow == -1) return;
  223.  
  224.     // load the next frame to decompress
  225.     mediaTimeNow = TrackTimeToMediaTime(timeNow, srcVideoTrack);
  226.     if (mediaTimeNow == -1) return;
  227.  
  228.     err = GetMediaSample(srcVideoMedia, srcVideoData1, 0, &srcVideoDataSize1, mediaTimeNow, nil, nil, nil, nil, 1, nil, nil);
  229.     if (err) return;
  230.  
  231.     // initialize more stuff
  232.     useFirstBufferForCompress = true;
  233.     currentCompressGWorldIndex = 0;
  234.     currentDecompressGWorldIndex = 1;
  235.  
  236.     while (timeNow < srcMovieDuration) {
  237.         TimeValue mediaTimeNow = TrackTimeToMediaTime(timeNow, srcVideoTrack);
  238.  
  239.         if( fPeriodic( w, &whiteRect, srcMovieDuration, timeNow ) )
  240.             break;
  241.  
  242.         if (mediaTimeNow != -1) {
  243.             // wait for last source frame to finish decompressing
  244.             while (gDecompressingSourceFrame)
  245.                 ;
  246.  
  247.             // start that next source frame decompressing
  248.             gDecompressingSourceFrame = true;
  249.             HLock(useFirstBufferForCompress ? srcVideoData1 : srcVideoData2);
  250.             err = DecompressSequenceFrameS(srcDecoSequences[currentDecompressGWorldIndex],
  251.                         useFirstBufferForCompress ? *srcVideoData1 : *srcVideoData2,
  252.                         useFirstBufferForCompress ? srcVideoDataSize1 : srcVideoDataSize2, 0, &outFlags, &srcDecoCompletion);
  253.             if (err) return;
  254.  
  255.             // read next source frame
  256.             HUnlock(useFirstBufferForCompress ? srcVideoData2 : srcVideoData1);
  257.             err = GetMediaSample(srcVideoMedia, useFirstBufferForCompress ? srcVideoData2 : srcVideoData1,
  258.                         0, useFirstBufferForCompress ? &srcVideoDataSize2 : &srcVideoDataSize1, mediaTimeNow, nil, nil, nil, nil, 1, nil, nil);
  259.             if (err) return;
  260.  
  261.             // wait for last compress to finish compressing
  262.             while (gCompressingSourceFrame)
  263.                 ;
  264.  
  265.             // figure out the data rate parameters for this next frame
  266.             if (sampleCount < MAX_SAMPLES) {
  267.                 SampleRecord *srp = &sampleRecords[sampleCount];
  268.                 srp->duration = 33;        //•• evil
  269.                 srp->size = useFirstBufferForCompress ? compressedDataSize2 : compressedDataSize1;
  270.                 totalDuration += 33;    //•• evil
  271.                 totalSize += srp->size;
  272.                 sampleCount++;
  273.             }
  274.  
  275.             // drop (totalDuration - 1000) worth of sample data
  276.             {
  277.                 long n = totalDuration - 1000;
  278.                 while (n && totalDuration && sampleCount) {
  279.                     if (n - sampleRecords[0].duration >= 0) {
  280.                         short i;
  281.                         n -= sampleRecords[0].duration;
  282.                         totalDuration -= sampleRecords[0].duration;
  283.                         totalSize -= sampleRecords[0].size;
  284.                         for (i = 1; i < sampleCount; i++)
  285.                             sampleRecords[i-1] = sampleRecords[i];
  286.                         sampleCount--;
  287.                     } else {
  288.                         long dec = n * sampleRecords[0].size / sampleRecords[0].duration;
  289.                         sampleRecords[0].size -= dec;
  290.                         sampleRecords[0].duration -= n;
  291.                         totalSize -= dec;
  292.                         totalDuration -= n;
  293.                         n = 0;
  294.                     }
  295.                 }
  296.             }
  297.  
  298.             dataRateStuff.dataOverrun = totalSize - dataRateStuff.dataRate;
  299.             dataRateStuff.frameDuration = 33;        //•• evil
  300.             SetCSequenceDataRateParams(compSequence, &dataRateStuff);
  301.  
  302.             if (screenDecoSequence) {
  303.                 // write out to disk the most recently compressed frame
  304.                 SampleEntryRecord ser;
  305.  
  306.                 ser.duration = 33;                //•• evil
  307.                 ser.fileOffset = currentFileOffset;
  308.                 ser.dataSize = useFirstBufferForCompress ? compressedDataSize2 : compressedDataSize1;
  309.                 ser.isKeyFrame = (useFirstBufferForCompress ? compressedSimilarity2 : compressedSimilarity1) == 0;
  310.                 err = PtrAndHand(&ser, (Handle)theSampleRefs, sizeof(ser));
  311.                 if (err) break;
  312.  
  313.                 // wait for previous write to complete
  314.                 while (writeBlock.ioResult == 1)
  315.                     ;
  316.  
  317.                 if (writeBlock.ioResult != noErr) return;
  318.  
  319.                 writeBlock.ioRefNum = outputFref;
  320.                 writeBlock.ioPosMode = fsFromStart;
  321.                 writeBlock.ioPosOffset = currentFileOffset;
  322.                 writeBlock.ioReqCount = ser.dataSize;
  323.                 writeBlock.ioBuffer = useFirstBufferForCompress ? *compressedData2 : *compressedData1;
  324.                 writeBlock.ioCompletion = nil;
  325.  
  326.                 PBWriteAsync((ParmBlkPtr)&writeBlock);
  327.  
  328.                 currentFileOffset += ser.dataSize;
  329.             }
  330.  
  331.             // compress last source frame
  332.             err = SetCSequencePrev(compSequence, gw[(currentCompressGWorldIndex + 2) % 3]->portPixMap, &gw[0]->portRect);
  333.             if (err) return;
  334.  
  335.             err = CompressSequenceFrame(compSequence, gw[currentCompressGWorldIndex]->portPixMap,
  336.                         nil, codecFlagUpdatePreviousComp, useFirstBufferForCompress ? *compressedData1 : *compressedData2,
  337.                         useFirstBufferForCompress ? &compressedDataSize1 : &compressedDataSize2,
  338.                         useFirstBufferForCompress ? &compressedSimilarity1 : &compressedSimilarity2,
  339.                         &compressCompletion);
  340.             if (err) return;
  341.  
  342.             // wait for decompress to screen frame to finish decompressing
  343.             while (gDecompressingToScreen)
  344.                 ;
  345.  
  346.             // probably decompress the last compressed frame to the screen
  347.             if (!screenDecoSequence) {
  348.                 // first time around, just make up the decompress sequence
  349.                 err = DecompressSequenceBegin(&screenDecoSequence, compressedDesc, (CGrafPtr)w, nil, nil, nil, ditherCopy, nil, 0, codecNormalQuality, anyCodec);
  350.                 if (err) return;
  351.             }
  352.             else {
  353.                 // all other times, decompress last compressed frame to screen
  354.                 gDecompressingToScreen = true;
  355.                 err = DecompressSequenceFrameS(screenDecoSequence,
  356.                             useFirstBufferForCompress ? *compressedData2 : *compressedData1,
  357.                             useFirstBufferForCompress ? compressedDataSize2 : compressedDataSize1, 0, &outFlags, &screenDecoCompletion);
  358.                 if (err) return;
  359.             }
  360.  
  361.             useFirstBufferForCompress = !useFirstBufferForCompress;
  362.             currentCompressGWorldIndex = (currentCompressGWorldIndex + 1) % 3;
  363.             currentDecompressGWorldIndex = (currentDecompressGWorldIndex + 1) % 3;
  364.         }
  365.  
  366.         GetTrackNextInterestingTime(srcVideoTrack, nextTimeMediaSample | nextTimeIgnoreActiveSegment, timeNow, 1, &timeNow, nil);
  367.         if (timeNow == -1)
  368.             timeNow = srcMovieDuration;
  369.     }
  370.  
  371.     fPeriodic( w, &whiteRect, srcMovieDuration, timeNow );
  372.  
  373.     {
  374.     long sampleCount = GetHandleSize((Handle)theSampleRefs) / sizeof(SampleEntryRecord);
  375.     long i;
  376.     Track dstVideoTrack;
  377.     Media dstVideoMedia;
  378.  
  379.     dstVideoTrack = NewMovieTrack(newMovie, (**compressedDesc).width << 16, (**compressedDesc).height << 16, 0);
  380.     dstVideoMedia = NewTrackMedia(dstVideoTrack, 'vide', 1000, nil, 0);
  381.  
  382.     for (i=0; i<sampleCount; i++) {
  383.         SampleEntryRecord ser = (*theSampleRefs)[i];
  384.  
  385.         err = AddMediaSampleReference(dstVideoMedia, ser.fileOffset, ser.dataSize, ser.duration, (SampleDescriptionHandle)compressedDesc,
  386.                 1, ser.isKeyFrame ? 0 : mediaSampleNotSync, nil);
  387.         if (err) return;
  388.     }
  389.  
  390.     err = InsertMediaIntoTrack(dstVideoTrack, 0, 0, GetMediaDuration(dstVideoMedia), 1 << 16);
  391.     if (err) return;
  392.  
  393.     // copy over all other tracks (by reference)
  394.     for (i=1; i<=GetMovieTrackCount(m); i++) {
  395.         Track t = GetMovieIndTrack(m, i);
  396.         Track newTrack;
  397.  
  398.         if (t == srcVideoTrack)
  399.             continue;
  400.  
  401.         AddEmptyTrackToMovie(t, newMovie, nil, 0, &newTrack);
  402.         InsertTrackSegment(t, newTrack, 0, GetMovieDuration(newMovie), 0);
  403.     }
  404.  
  405.     err = AddMovieResource(newMovie, outputResRef, nil, nil);
  406.     if (err) return;
  407.     }
  408.  
  409.     OpenSelection(&newFile);
  410.  
  411. bail:
  412.     CDSequenceEnd(screenDecoSequence);
  413.  
  414.     for (i=0; i<3; i++)
  415.         CDSequenceEnd(srcDecoSequences[i]);
  416.  
  417.     CDSequenceEnd(compSequence);
  418.  
  419.     if (outputFref) FSClose(outputFref);
  420.     if (outputResRef != -1) CloseMovieFile(outputResRef);
  421.  
  422.     if (newMovie) DisposeMovie(newMovie);
  423. }
  424.  
  425. pascal void decompressSrcFrameDone(OSErr /* result */, short flags, long refcon)
  426. {
  427.     Boolean *decompressingFlag = (Boolean *)refcon;
  428.  
  429.     if (flags & codecCompletionDest)
  430.         *decompressingFlag = false;
  431. }
  432.  
  433. pascal void compressSrcFrameDone(OSErr /* result */, short flags, long refcon)
  434. {
  435.     Boolean *compressingFlag = (Boolean *)refcon;
  436.  
  437.     if (flags & codecCompletionDest)
  438.         *compressingFlag = false;
  439. }
  440.  
  441. pascal void decompressToScreenDone(OSErr /* result */, short flags, long refcon)
  442. {
  443.     Boolean *decompressingFlag = (Boolean *)refcon;
  444.     if (flags & codecCompletionDest)
  445.         *decompressingFlag = false;
  446. }
  447.  
  448. /*==================================================================================*/
  449. Boolean fPeriodic( WindowRef theWindow, Rect *whiteRect, TimeValue srcMovieDuration, TimeValue timeNow ) {
  450. /* Check for events, update progress bar. Return true to quit program.                */
  451.  
  452.     #define kTimeStamps 30
  453.  
  454.     long i;
  455.     Boolean quit;
  456.     EventRecord theEvent;
  457.     RGBColor theColor;
  458.     float framesPerSecond;
  459.     long startTick;
  460.     long endTick;
  461.     Str255 theString;
  462.     WindowRef aWindow;
  463.     long startMovieTime;
  464.     static gSetup;
  465.     static Rect gTheRect;
  466.     static Rect gTextRect;
  467.     static Rect gMPTextRect;
  468.     static Rect gTimeRemainingTextRect;
  469.     static Rect gFillRect;
  470.     static long gTimeStamps[kTimeStamps];
  471.     static long gNextStamp;
  472.     unsigned long gInitialTick;
  473.     static long gMovieTimes[kTimeStamps];
  474.  
  475.     quit = false;
  476.  
  477.     /* Check for events */
  478.     if( OSEventAvail( mDownMask | keyDownMask, &theEvent) ) {
  479.         GetNextEvent(mDownMask | keyDownMask, &theEvent);
  480.         switch( theEvent.what ) {
  481.             case mouseDown:
  482.                 if (FindWindow( theEvent.where, &aWindow ) == inGoAway) {
  483.                     if (TrackGoAway(aWindow, theEvent.where))
  484.                         quit = true;
  485.                 }
  486.                 break;
  487.             case keyDown:
  488.                 if( theEvent.modifiers & cmdKey )
  489.                     if( (char)theEvent.message == 'Q' ||
  490.                         (char)theEvent.message == 'q' ||
  491.                         (char)theEvent.message == '.' )
  492.                         quit = true;
  493.                 break;
  494.             }
  495.         }
  496.  
  497.     /* Draw progress the first time */
  498.     if( !gSetup ) {
  499.         gTheRect = *whiteRect;
  500.         gTheRect.left += 20;
  501.         gTheRect.bottom -= 20;
  502.         gTheRect.top = gTheRect.bottom - 20;
  503.         gTheRect.right -= 80;
  504.         gTextRect = gTheRect;
  505.         OffsetRect( &gTextRect, 0, -40 );
  506.         gMPTextRect = gTheRect;
  507.         OffsetRect( &gMPTextRect, 0, -60 );
  508. #if 0
  509.         gTimeRemainingTextRect = gTheRect;
  510.         OffsetRect( &gTimeRemainingTextRect, 0, -40 );
  511. #endif
  512.         FrameRect( &gTheRect );
  513.         InsetRect( &gTheRect, 1, 1 );
  514.         gFillRect = gTheRect;
  515.         if( theWindow->portBits.rowBytes < 0 )
  516.             if( (*((CGrafPtr)theWindow)->portPixMap)->pixelSize > 1 ) {
  517.                 fSetRGBColor( &theColor, 205, 205, 255 );
  518.                 RGBForeColor( &theColor );
  519.                 PaintRect( &gFillRect );
  520.                 fSetRGBColor( &theColor, 0, 0, 0 );
  521.                 RGBForeColor( &theColor );
  522.                 }
  523.  
  524.         for( i = 0; i < kTimeStamps; i++ )
  525.             gTimeStamps[i] = -1;
  526.         gNextStamp = 0;
  527.  
  528.         MoveTo( gTextRect.left, gTextRect.bottom );
  529.         DrawString( "\pFrames per second: " );
  530.         gTextRect.left += StringWidth( "\pFrames per second: " );
  531.  
  532.         MoveTo( gMPTextRect.left, gMPTextRect.bottom );
  533.         DrawString( "\pProcessors in use: " );
  534.         gMPTextRect.left += StringWidth( "\pProcessors in use: " );
  535. #if 0
  536.         MoveTo( gTimeRemainingTextRect.left, gTimeRemainingTextRect.bottom );
  537.         DrawString( "\pTime remaining: " );
  538.         gTimeRemainingTextRect.left += StringWidth( "\pTime remaining: " );
  539. #endif
  540.         gInitialTick = LMGetTicks();
  541.  
  542.         gSetup = true;
  543.         }
  544.  
  545.     /* Update progress the rest of the time */
  546.     else {
  547.  
  548.         gFillRect.right = gTheRect.left + (gTheRect.right - gTheRect.left) *
  549.             timeNow / (float)srcMovieDuration;
  550.         fSetRGBColor( &theColor, 86, 86, 86 );
  551.         RGBForeColor( &theColor );
  552.         PaintRect( &gFillRect );
  553.         fSetRGBColor( &theColor, 0, 0, 0 );
  554.         RGBForeColor( &theColor );
  555.         TextMode(srcCopy);
  556.         TextFace(bold);
  557.  
  558.         startTick = gTimeStamps[gNextStamp];
  559.         endTick = LMGetTicks();
  560.         startMovieTime = gMovieTimes[gNextStamp];
  561.         gMovieTimes[gNextStamp] = timeNow;
  562.         gTimeStamps[gNextStamp++] = endTick;
  563.         if( gNextStamp == kTimeStamps )
  564.             gNextStamp = 0;
  565.         if( startTick != -1 ) {
  566.             framesPerSecond = (kTimeStamps / (float)(endTick - startTick)) * 60.0;
  567.             fPNumToString( framesPerSecond, theString, 2 );
  568.             MoveTo( gTextRect.left, gTextRect.bottom );
  569.             DrawString( theString );
  570.             DrawString( "\p      " );
  571.             }
  572.         MoveTo( gMPTextRect.left, gMPTextRect.bottom );
  573.         if (*(char *)0x17b & 2)
  574.             DrawString("\p1 ");
  575.         else
  576.             DrawString("\p4 ");
  577.  
  578. #if 0
  579.         if( startTick != -1 ) {
  580.             float percentOfTotal = (timeNow - startMovieTime) / (float)srcMovieDuration;
  581.             float totalDuration = (1.0 / percentOfTotal) * (endTick - startTick);
  582.             float ticksRemaining = totalDuration * (1.0 - ((float)timeNow / (float)srcMovieDuration));
  583.             float secondsRemaining = ticksRemaining / 60;
  584.             if (*(char *)0x17b)
  585.                 ticksRemaining += 0.00001;
  586.  
  587.             fPNumToString( secondsRemaining, theString, 2 );
  588.             MoveTo( gTimeRemainingTextRect.left, gTimeRemainingTextRect.bottom );
  589.             DrawString(theString);
  590.             DrawString( "\p      " );
  591.         }
  592. #endif
  593.     }
  594.  
  595.     return( quit );
  596.     }
  597.  
  598. /*==================================================================================*/
  599. void fSetRGBColor( RGBColor *theColor, short r, short g, short b ) {
  600.  
  601.     theColor->red = r * 256;
  602.     theColor->green = g * 256;
  603.     theColor->blue = b * 256;
  604.     }
  605.  
  606. /*==================================================================================*/
  607. void fPNumToString( float theNum, Str255 theString, short fix ) {
  608.  
  609.     short i;
  610.     Boolean negative;
  611.     long wholeNum;
  612.     float fraction;
  613.     long fractNum;
  614.     Str255 fractString;
  615.     short padCount;
  616.  
  617.     negative = false;
  618.     if( theNum < 0 ) {
  619.         negative = true;
  620.         theNum = -theNum;
  621.         }
  622.  
  623.     /* Round the number off before proceeding */
  624.     fraction = 0.500001;
  625.     for( i = 0; i < fix; i++ )
  626.         fraction /= 10;
  627.     theNum += fraction;
  628.  
  629.     /* Set up whole number portion of the string */
  630.     wholeNum = theNum;
  631.  
  632.     /* Set up fractional portion of string */
  633.     fraction = theNum - wholeNum;
  634.     for( i = 0; i < fix; i++ )
  635.         fraction *= 10;
  636.     fractNum = fraction;
  637.  
  638.     /* Build the string */
  639.     NumToString( wholeNum, theString );
  640.     theString[++theString[0]] = '.';
  641.     NumToString( fractNum, fractString );
  642.     padCount = fix - fractString[0];
  643.     for( i = 0; i < padCount; i++ )
  644.         theString[++theString[0]] = '0';
  645.     fPStrConcat( theString, fractString );
  646.  
  647.     /* Strip trailing zero's (except the one after the decimal place if any) */
  648.     while( theString[theString[0]] == '0' &&
  649.         theString[theString[0] - 1] != '.' &&
  650.         theString[0] > 3 )
  651.         theString[0]--;
  652.  
  653.     if( negative ) {
  654.         BlockMove( &theString[1], &theString[2], theString[0] );
  655.         theString[1] = '-';
  656.         theString[0]++;
  657.         }
  658.     }
  659.  
  660. /*==================================================================================*/
  661. void fPStrCopy( Str255 s1, Str255 s2 ) {
  662. /* Copies pascal string s2 into s1 (bowing to strcpy() parameter ordering).            */
  663.  
  664.     BlockMove( s2, s1, 255 < s2[0] + 1 ? 255 : s2[0] + 1 );
  665.     }
  666.  
  667. /*==================================================================================*/
  668. void fPStrConcat( Str255 s1, Str255 s2 ) {
  669. /* Attaches pascal string s2 to pascal string s1.                                    */
  670.  
  671.     short i;
  672.  
  673.     i = 255 - s1[0] < s2[0] ? 255 - s1[0] : s2[0];
  674.     BlockMove( &s2[1], &s1[s1[0] + 1], i );
  675.     s1[0] += i;
  676.     }
  677.  
  678. /*==================================================================================*/
  679.  
  680.  
  681. pascal OSErr AEOpenDoc(AppleEvent *message, AppleEvent *reply,long refCon);
  682. OSErr MissedAEParameters (AppleEvent *message);
  683.  
  684. Boolean getMovieFile(FSSpec *theFile)
  685. {
  686.     Boolean result = false;
  687.     unsigned long gStartTicks = LMGetTicks();
  688.     void *upp = NewAEEventHandlerProc(AEOpenDoc);
  689.  
  690.     theFile->vRefNum = 0;
  691.  
  692.     AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, upp, (long)theFile, false);
  693.  
  694.     while (gStartTicks + 30 > (unsigned long)LMGetTicks()) {
  695.         EventRecord event;
  696.  
  697.         WaitNextEvent(everyEvent, &event, 0, 0);
  698.         if (event.what == kHighLevelEvent)
  699.             AEProcessAppleEvent(&event);
  700.     }
  701.  
  702.     AERemoveEventHandler(kCoreEventClass, kAEOpenDocuments, upp, false);
  703.  
  704.     if (theFile->vRefNum == 0) {
  705.         StandardFileReply reply;
  706.         SFTypeList types;
  707.  
  708.         types[0] = MovieFileType;
  709.         StandardGetFilePreview(nil, 1, types, &reply);
  710.  
  711.         result = reply.sfGood;
  712.         if (result)
  713.             *theFile = reply.sfFile;
  714.     }
  715.     else
  716.         result = true;
  717.  
  718.     return result;
  719. }
  720.  
  721. pascal OSErr AEOpenDoc(AppleEvent *message, AppleEvent *,long refCon)
  722. {
  723.     FSSpec fss;
  724.     AEDescList docList;
  725.     long index, itemsInList;
  726.     Size actualSize;
  727.     AEKeyword keywd;
  728.     DescType typeCode;
  729.     OSErr err;
  730.     FSSpec *filePtr = (FSSpec *)refCon;
  731.  
  732.     err = AEGetParamDesc(message, keyDirectObject, typeAEList, &docList);
  733.     if(err) goto bail;
  734.  
  735.     err = MissedAEParameters(message);
  736.     if(err) goto bail;
  737.  
  738.     err = AECountItems(&docList, &itemsInList);
  739.     if(err) goto bail;
  740.  
  741.     for (index = 1; index <= itemsInList; index++) {
  742.         HFileInfo pb;
  743.  
  744.         err = AEGetNthPtr(&docList, index, typeFSS, &keywd, &typeCode,
  745.                     (Ptr)&fss, sizeof(FSSpec), &actualSize);
  746.         if(err) goto bail;
  747.  
  748.         // need to figure out if this is a directory or a file
  749.  
  750.         pb.ioVRefNum = fss.vRefNum;
  751.         pb.ioFDirIndex = 0;
  752.         pb.ioDirID = fss.parID;
  753.         pb.ioNamePtr = fss.name;
  754.         err = PBGetCatInfoSync((void *)&pb);
  755.         if (!err) {
  756.             if (pb.ioFlAttrib & 16) {
  757.                 fss.parID = pb.ioDirID;
  758.             }
  759.             else {
  760.                 *filePtr = fss;
  761.                 break;
  762.             }
  763.         }
  764.     }
  765.  
  766.     err = AEDisposeDesc(&docList);
  767.  
  768. bail:
  769.     return err;
  770. }
  771.  
  772. OSErr MissedAEParameters (AppleEvent *message)
  773.     {
  774.     DescType typeCode;
  775.     Size actualSize;
  776.     OSErr err;
  777.  
  778.     err = AEGetAttributePtr(message, keyMissedKeywordAttr, typeWildCard,
  779.             &typeCode, nil, 0L, &actualSize);
  780.     if (err == errAEDescNotFound)
  781.         return(noErr);
  782.     return(err = noErr ? errAEEventNotHandled : err);
  783.     }
  784.  
  785. OSErr FindAProcess(OSType typeToFind, OSType creatorToFind, ProcessSerialNumberPtr processSN);
  786.  
  787. /* OpenSelection takes a FSSpec pointer, and creates a Finder Open Selection */
  788. /* AppleEvent for the document described by the FSSpec.  This can be an */
  789. /* application or document. */
  790. OSErr OpenSelection(FSSpecPtr theDocToOpen)
  791. {
  792.     OSErr err;
  793.     AppleEvent aeEvent;
  794.     AEDesc myAddressDesc, aeDirDesc, listElem;
  795.     FSSpec dirSpec;
  796.     AEDesc fileList;
  797.     ProcessSerialNumber process;
  798.     AliasHandle DirAlias = nil, FileAlias = nil;
  799.  
  800.     /* go find the Finder's process information, please */
  801.     if (FindAProcess('FNDR', 'MACS', &process))
  802.         return procNotFound;
  803.  
  804.     myAddressDesc.dataHandle = nil;
  805.     aeDirDesc.dataHandle = nil;
  806.     listElem.dataHandle = nil;
  807.     fileList.dataHandle = nil;
  808.     aeEvent.dataHandle = nil;
  809.  
  810.     /* Create an address descriptor so the AppleEvent manager knows where to send this event */
  811.     if (err = AECreateDesc(typeProcessSerialNumber, (Ptr)&process, sizeof(process), &myAddressDesc))
  812.         goto bail;
  813.  
  814.     /* Create the empty FinderEvent */
  815.     /* it's a Finder 'FNDR', Open Selection 'sope' event */
  816.     if (err = AECreateAppleEvent('FNDR', 'sope', &myAddressDesc, kAutoGenerateReturnID, kAnyTransactionID, &aeEvent))
  817.         goto bail;
  818.  
  819.     /* make a FSSpec for the parent folder (see the OpenSelection description in the AE Registry ) */
  820.     /* using the information in the document FSSpec */
  821.     if (err = FSMakeFSSpec(theDocToOpen->vRefNum, theDocToOpen->parID, nil, &dirSpec))
  822.         goto bail;
  823.     if (err = NewAliasMinimal(&dirSpec, &DirAlias))
  824.         goto bail;
  825.  
  826.     /* Create alias for file */
  827.     if (err = NewAliasMinimal(theDocToOpen, &FileAlias))
  828.         goto bail;
  829.  
  830.     /* Create the file  list */
  831.     if (err = AECreateList(nil, 0, false, &fileList))
  832.         goto bail;
  833.  
  834.     /*  create the folder  descriptor */
  835.     HLock((Handle)DirAlias);
  836.         err = AECreateDesc(typeAlias, (void *)*DirAlias, GetHandleSize((Handle)DirAlias), &aeDirDesc);
  837.         if (err) goto bail;
  838.     DisposeHandle((Handle)DirAlias);
  839.     DirAlias = nil;
  840.  
  841.     /* put the Directory Desc in the event as the direct object */
  842.     if ((err = AEPutParamDesc(&aeEvent, keyDirectObject, &aeDirDesc)) == noErr) {
  843.         AEDisposeDesc(&aeDirDesc);
  844.         aeDirDesc.dataHandle = nil;
  845.  
  846.         /*  create the file descriptor and add to aliasList */
  847.         HLock((Handle)FileAlias);
  848.             err = AECreateDesc(typeAlias, (void *)*FileAlias, GetHandleSize((Handle)FileAlias), &listElem);
  849.             if (err) goto bail;
  850.         DisposeHandle((Handle)FileAlias);
  851.         FileAlias = nil;
  852.  
  853.         err = AEPutDesc(&fileList, 0, &listElem);
  854.         if (err) goto bail;
  855.     }
  856.  
  857.     AEDisposeDesc(&listElem);
  858.     listElem.dataHandle = nil;
  859.  
  860.     /* Add the file alias list to the event */
  861.     if (err = AEPutParamDesc(&aeEvent, 'fsel', &fileList))
  862.         goto bail;
  863.  
  864.     AEDisposeDesc(&fileList);
  865.     fileList.dataHandle = nil;
  866.  
  867.     /* And now send the event! */
  868.     err = AESend(&aeEvent, nil, kAENoReply | kAENeverInteract | kAECanSwitchLayer, kAENormalPriority, kAEDefaultTimeout, nil, nil);
  869.     if (err == noPortErr) {
  870.         err = AESend(&aeEvent, nil, kAENoReply | kAENeverInteract | kAECanSwitchLayer, kAENormalPriority, kAEDefaultTimeout, nil, nil);
  871.     }
  872.  
  873. bail:
  874.     AEDisposeDesc(&aeEvent);
  875.     AEDisposeDesc(&myAddressDesc);
  876.     AEDisposeDesc(&aeDirDesc);
  877.     AEDisposeDesc(&listElem);
  878.     AEDisposeDesc(&fileList);
  879.     AEDisposeDesc(&aeEvent);
  880.     DisposeHandle((Handle)DirAlias);
  881.     DisposeHandle((Handle)FileAlias);
  882.  
  883.     return err;
  884. }
  885.  
  886.  
  887. OSErr FindAProcess(OSType typeToFind, OSType creatorToFind, ProcessSerialNumberPtr processSN)
  888. {
  889.     OSErr err;
  890.     ProcessInfoRec tempInfo;
  891.  
  892.     processSN->lowLongOfPSN = kNoProcess;
  893.     processSN->highLongOfPSN = kNoProcess;
  894.  
  895.     tempInfo.processInfoLength = sizeof(ProcessInfoRec);
  896.     tempInfo.processName = nil;
  897.     tempInfo.processAppSpec = nil;
  898.  
  899.     do {
  900.         if (err = GetNextProcess(processSN))
  901.             break;
  902.  
  903.         GetProcessInformation(processSN, &tempInfo);
  904.     }  while ((tempInfo.processSignature != creatorToFind) && (tempInfo.processType != typeToFind));
  905.  
  906.     return err;
  907. }
  908.